package com.example.testcamara;

import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.Camera;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.ImageReader;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Display;
import android.view.OrientationEventListener;
import android.view.Surface;
import android.view.ViewGroup;
import android.view.WindowManager;

import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;

import java.io.File;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;


public class VideoRecordCameraController {
    private static final String TAG = "CameraController";

    @Retention(RetentionPolicy.SOURCE)
    @IntDef({PHONE_HOME_BUTTON_BOTTOM, PHONE_HOME_BUTTON_TOP, PHONE_HOME_BUTTON_LEFT, PHONE_HOME_BUTTON_RIGHT})
    public @interface PhoneOrientation {
    }

    /**
     * 竖屏（home键在底部）
     */
    public static final int PHONE_HOME_BUTTON_BOTTOM = 0;
    /**
     * 反向竖屏（home键在顶部）
     */
    public static final int PHONE_HOME_BUTTON_TOP = 1;
    /**
     * 横屏（home键在左边）
     */
    public static final int PHONE_HOME_BUTTON_LEFT = 2;
    /**
     * 横屏（home键在右边）
     */
    public static final int PHONE_HOME_BUTTON_RIGHT = 3;

    public static final String CAMERA_POSITION_BACK = "back";
    public static final String CAMERA_POSITION_FRONT = "front";

    private static final String ALC_START_RECORDING = "startRecording";
    private static final String ALC_STOP_RECORDING = "stopRecording";
    private static final String ALC_CLOSE_PREVIEW_SESSION = "closePreviewSession";
    private static final String ALC_START_PREVIEW = "startPreview";
    private static final String ALC_SWITCH_CAMERA = "switchCamera";
    private static final String ALC_CLOSE_CAMERA = "closeCamera";
    private static final String ALC_OPEN_CAMERA = "openCamera";
    private static final String ALC_SETUP_CAMERA_OUTPUTS = "setUpCameraOutputs";
    private static final String ALC_UPDATE_PREVIEW = "updatePreview";
    private static final String ALC_KEY_MSG = "msg";

    private final Context mContext;
    private VideoRecordTextureView mTextureView;
    private Handler mBackgroundHandler;
    private String mVideoSaveDirPath;
    private String mVideoFilePath;
    private CameraDevice mCameraDevice;
    private Size mPreviewSize;
    private CaptureRequest.Builder mPreviewBuilder;
    private CaptureRequest.Builder mPreviewRequestBuilder;
    private MediaRecorder mMediaRecorder;
    private CameraCaptureSession mPreviewSession;
    private Integer mSensorOrientation;
    private CameraCaptureSession mCaptureSession;
    private String mCameraId;
    private ImageReader mImageReader;
    private CaptureRequest mPreviewRequest;
    private static final int STATE_PREVIEW = 0;
    private static final int STATE_WAITING_LOCK = 1;
    private static final int STATE_WAITING_PRE_CAPTURE = 2;
    private static final int STATE_WAITING_NON_PRE_CAPTURE = 3;
    private static final int STATE_PICTURE_TAKEN = 4;
    private int mState = STATE_PREVIEW;
    private final Semaphore mCameraOpenCloseLock = new Semaphore(1);
    private static final int MAX_PREVIEW_HEIGHT = 1280;
    private static final int MAX_PREVIEW_WIDTH = 720;
    private static final SparseIntArray ORIENTATION = new SparseIntArray();
    private OnVideoRecordListener mVideoRecordListener;
    private OrientationEventListener mRotationListener;
    @PhoneOrientation
    private int mPhoneOrientation = PHONE_HOME_BUTTON_BOTTOM;
    private OnPhoneOrientationChangeListener mOnPhoneOrientationChangeListener;
    private final AtomicBoolean mIsRecording = new AtomicBoolean(false);
    /**
     * 切换摄像头
     */
    private final AtomicBoolean mIsSwitchingCamera = new AtomicBoolean(false);
    /**
     * 闪光灯
     */
    private final AtomicBoolean mFlashlightOn = new AtomicBoolean(false);
    private String mCameraPosition = CAMERA_POSITION_FRONT;
    private int mOriginTextureWidth, mOriginTextureHeight;

    static {
        ORIENTATION.append(Surface.ROTATION_0, 90);
        ORIENTATION.append(Surface.ROTATION_90, 0);
        ORIENTATION.append(Surface.ROTATION_180, 270);
        ORIENTATION.append(Surface.ROTATION_270, 180);
    }

    public VideoRecordCameraController(Context context, VideoRecordTextureView textureView) {
        mContext = context;
        this.mTextureView = textureView;
        init();
    }

    public void init() {
        startBackgroundThread();

        mRotationListener = new OrientationEventListener(mContext) {
            @Override
            public void onOrientationChanged(int orientation) {
                onOrientationRotate(orientation);
            }
        };
        mRotationListener.enable();

        initSaveDir();
    }

    private void initSaveDir() {
        mVideoSaveDirPath = mContext.getExternalCacheDir().getPath() + "/videorecord";
        File folder = new File(mVideoSaveDirPath);
        if (!folder.exists()) {
            //noinspection ResultOfMethodCallIgnored
            folder.mkdir();
        }
    }

    public void initCamera() {
        if (mTextureView.isAvailable()) {
            mOriginTextureWidth = mTextureView.getWidth();
            mOriginTextureHeight = mTextureView.getHeight();
            openCamera(mTextureView.getWidth(), mTextureView.getHeight());
        } else {
            mTextureView.setSurfaceTextureListener(mSurfaceTextureListener);
        }
    }

    public void setOnPhoneOrientationChangeListener(OnPhoneOrientationChangeListener listener) {
        this.mOnPhoneOrientationChangeListener = listener;
    }

    /**
     * 手机旋转
     */
    private void onOrientationRotate(int orientation) {
        if (orientation == OrientationEventListener.ORIENTATION_UNKNOWN) {
            return;
        }
        // 根据设备方向进行相应的处理
        if (orientation >= 0 && orientation < 45 || orientation >= 315) {
            // 竖屏
            mPhoneOrientation = PHONE_HOME_BUTTON_BOTTOM;
        } else if (orientation >= 45 && orientation < 135) {
            // 左横屏
            mPhoneOrientation = PHONE_HOME_BUTTON_LEFT;
        } else if (orientation >= 135 && orientation < 225) {
            // 反向竖屏
            mPhoneOrientation = PHONE_HOME_BUTTON_TOP;
        } else if (orientation >= 225 && orientation < 315) {
            // 右横屏
            mPhoneOrientation = PHONE_HOME_BUTTON_RIGHT;
        }
        if (mOnPhoneOrientationChangeListener != null) {
            mOnPhoneOrientationChangeListener.onPhoneOrientationChanged(mPhoneOrientation);
        }
    }

    public void setVideoRecordListener(OnVideoRecordListener listener) {
        mVideoRecordListener = listener;
    }

    public void startRecordingVideo() {
        if (mIsRecording.get()) {
            return;
        }
        if (null == mCameraDevice || null == mPreviewSize || !mTextureView.isAvailable()) {
            return;
        }
        try {
            closePreviewSession();
            setUpMediaRecorder();
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);

            List<Surface> surfaces = new ArrayList<>();
            Surface previewSurface = new Surface(texture);
            surfaces.add(previewSurface);
            mPreviewBuilder.addTarget(previewSurface);

            Surface recorderSurface = mMediaRecorder.getSurface();
            surfaces.add(recorderSurface);
            mPreviewBuilder.addTarget(recorderSurface);

            mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                    mPreviewSession = cameraCaptureSession;
                    updatePreview();
                    new Handler(Looper.getMainLooper()).post(new Runnable() {
                        @Override
                        public void run() {
                            mMediaRecorder.start();
                        }
                    });
                }

                private void updatePreview() {
                    if (null == mCameraDevice) {
                        return;
                    }
                    try {
                        mPreviewBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
                        mPreviewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
                        if (mFlashlightOn.get()) {
                            mPreviewBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_TORCH);
                        } else {
                            mPreviewBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.FLASH_MODE_OFF);
                        }
                        mPreviewSession.setRepeatingRequest(mPreviewBuilder.build(), new CameraCaptureSession.CaptureCallback() {
                            @Override
                            public void onCaptureStarted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, long timestamp, long frameNumber) {
                                super.onCaptureStarted(session, request, timestamp, frameNumber);
                                if (!mIsRecording.getAndSet(true)) {
                                    if (mVideoRecordListener != null) {
                                        mVideoRecordListener.onStart();
                                    }
                                }
                            }
                        }, mBackgroundHandler);
                    } catch (Exception e) {
                    }
                }

                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                }
            }, mBackgroundHandler);
        } catch (Exception e) {
        }
    }

    public void stopRecordingVideo() {
        closePreviewSession();

        resetMediaRecorder();
        if (mVideoRecordListener != null && !TextUtils.isEmpty(mVideoFilePath)) {
            mVideoRecordListener.onComplete(mVideoFilePath);
        }

        startPreview();
    }

    private void resetMediaRecorder() {
        if (mMediaRecorder != null) {
            try {
                mMediaRecorder.stop();
                mMediaRecorder.reset();
            } catch (Exception e) {
            }
        }
        mIsRecording.set(false);
    }

    private void closePreviewSession() {
        if (mPreviewSession != null) {
            try {
                mPreviewSession.close();
            } catch (Exception e) {
            }
        }
    }

    public void onPause() {
        resetMediaRecorder();
        closeCamera();
        closePreviewSession();
    }

    public void onRestart() {
        initCamera();
    }

    private void setUpMediaRecorder() throws IOException {
        mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
        mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
        mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
        mVideoFilePath = createVideoFilePath();
        mMediaRecorder.setOutputFile(mVideoFilePath);
        // 码流率 1M
        mMediaRecorder.setVideoEncodingBitRate(2000000);
        mMediaRecorder.setVideoFrameRate(25);
        mMediaRecorder.setVideoSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
        mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
        mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
        mMediaRecorder.setOrientationHint(getOrientationForMediaRecord());
        mMediaRecorder.prepare();
    }

    private int getOrientationForMediaRecord() {
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        Camera.getCameraInfo(getCameraIdIntValue(mCameraId), cameraInfo);
        int orientation = mContext.getResources().getConfiguration().orientation;
        // Activity竖屏模式
        if (orientation == Configuration.ORIENTATION_PORTRAIT) {
            // Home键在左右，即手机横置
            if (mPhoneOrientation == PHONE_HOME_BUTTON_RIGHT || mPhoneOrientation == PHONE_HOME_BUTTON_LEFT) {
                return 0;
            } else {
                // 前置摄像头
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    return 270;
                }
            }
        }
        // Activity横屏模式
        else {
            // Home键在上下，即手机纵置
            if (mPhoneOrientation == PHONE_HOME_BUTTON_BOTTOM || mPhoneOrientation == PHONE_HOME_BUTTON_TOP) {
                // 前置摄像头
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                    return 270;
                } else {
                    return 90;
                }
            }
        }
        int rotation = getWindowDisplay().getRotation();
        return getOrientation(rotation);
    }

    private String createVideoFilePath() {
        File pathVideo = new File(mVideoSaveDirPath);
        if (!pathVideo.exists()) {
            //noinspection ResultOfMethodCallIgnored
            pathVideo.mkdir();
        }
        String fileName = getNowDate();
        return new File(mVideoSaveDirPath, fileName + ".mp4").getAbsolutePath();
    }

    /**
     * 打开/关闭闪光灯
     */
    public void toggleFlashlight(boolean enable) {
        if (mIsRecording.get()) {
            return;
        }
        mFlashlightOn.set(enable);
    }

    public void switchCamera() {
        if (TextUtils.equals(mCameraPosition, CAMERA_POSITION_FRONT)) {
            switchCamera(CAMERA_POSITION_BACK);
        } else {
            switchCamera(CAMERA_POSITION_FRONT);
        }
    }

    /**
     * 切换前置/后置摄像头
     */
    public void switchCamera(String cameraPosition) {
        mCameraPosition = cameraPosition;
        if (!mTextureView.isAvailable()) {
            return;
        }
        if (mCameraDevice == null) {
            return;
        }
        if (mIsRecording.get()) {
            return;
        }
        if (mIsSwitchingCamera.getAndSet(true)) {
            return;
        }

        if (mTextureView.isAvailable()) {
            Log.e("EEED", "mTextureView current W=" + mTextureView.getWidth() + " H=" + mTextureView.getHeight());
            Log.e("EEED", "mTextureView origin W=" + mOriginTextureWidth + " H=" + mOriginTextureHeight);
//            setUpCameraOutputs(mOriginTextureWidth, mOriginTextureHeight);
        }

        // 关闭当前相机
        closeCamera();

        // 切换相机
        String newCameraId;
        if (TextUtils.equals(mCameraId, String.valueOf(CameraCharacteristics.LENS_FACING_BACK))) {
            newCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_FRONT);
        } else {
            newCameraId = String.valueOf(CameraCharacteristics.LENS_FACING_BACK);
        }

        // 打开新相机
        openCamera(newCameraId);

        mIsSwitchingCamera.set(false);
    }

    private int getCameraIdIntValue(String cameraId) {
        try {
            return Integer.parseInt(cameraId);
        } catch (Exception e) {
            return CameraCharacteristics.LENS_FACING_BACK;
        }
    }

    private void openCamera(String cameraId) {
        try {
            mCameraId = cameraId;
            CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA)
                    != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(cameraId, mStateCallback, mBackgroundHandler);
        } catch (Exception e) {
        }
    }

    private void closeCamera() {
        if (mCaptureSession != null) {
            try {
                mCaptureSession.close();
            } catch (Exception e) {
            }
        }
        if (mCameraDevice != null) {
            try {
                mCameraDevice.close();
            } catch (Exception e) {
            }
        }
    }

    public void release() {
        mRotationListener.disable();
        mRotationListener = null;

        if (mMediaRecorder != null) {
            try {
                mMediaRecorder.stop();
                mMediaRecorder.reset();
                mMediaRecorder.release();
            } catch (Exception e) {
            }
            mMediaRecorder = null;
        }

        closePreviewSession();
        mPreviewSession = null;

        closeCamera();
        mCaptureSession = null;
        mCameraDevice = null;
    }

    private String getNowDate() {
        @SuppressLint("SimpleDateFormat")
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");
        return simpleDateFormat.format(new Date());
    }

    private final VideoRecordTextureView.SurfaceTextureListener mSurfaceTextureListener = new VideoRecordTextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
            if (mOriginTextureWidth <= 0 || mOriginTextureHeight <= 0) {
                mOriginTextureWidth = mTextureView.getWidth();
                mOriginTextureHeight = mTextureView.getHeight();
            }
            openCamera(width, height);
        }

        @Override
        public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surfaceTexture) {
            return true;
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
            // 在SurfaceTexture尺寸发生变化时调整相机预览尺寸
            configureTransform(width, height);
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surface) {
        }
    };

    private void startBackgroundThread() {
        HandlerThread mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }

    private void openCamera(int width, int height) {
        setUpCameraOutputs(width, height);
        CameraManager manager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        try {
            if (!mCameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
                return;
            }
            if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.CAMERA)
                    != PackageManager.PERMISSION_GRANTED) {
                return;
            }
            manager.openCamera(mCameraId, mStateCallback, mBackgroundHandler);
        } catch (Exception e) {
        }
    }

    private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
        @Override
        public void onOpened(@NonNull CameraDevice cameraDevice) {
            mCameraOpenCloseLock.release();
            mCameraDevice = cameraDevice;
            startPreview();
        }

        @Override
        public void onDisconnected(@NonNull CameraDevice cameraDevice) {
            mCameraOpenCloseLock.release();
            if (mCameraDevice != null) {
                mCameraDevice.close();
            }
            mCameraDevice = null;
        }

        @Override
        public void onError(@NonNull CameraDevice cameraDevice, int error) {
            mCameraOpenCloseLock.release();
            if (mCameraDevice != null) {
                mCameraDevice.close();
            }
            mCameraDevice = null;
        }
    };

    public void startPreview() {
        try {
            SurfaceTexture texture = mTextureView.getSurfaceTexture();
            texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
            Surface surface = new Surface(texture);
            mPreviewRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mPreviewRequestBuilder.addTarget(surface);
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            mPreviewRequestBuilder.set(CaptureRequest.FLASH_MODE, CaptureResult.FLASH_MODE_OFF);
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);

            mCameraDevice.createCaptureSession(Arrays.asList(surface, mImageReader.getSurface()),
                    new CameraCaptureSession.StateCallback() {
                        @Override
                        public void onConfigured(@NonNull CameraCaptureSession cameraCaptureSession) {
                            if (null == mCameraDevice) {
                                return;
                            }
                            mCaptureSession = cameraCaptureSession;
                            mPreviewRequest = mPreviewRequestBuilder.build();
                            try {
                                mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
                            } catch (Exception e) {
                            }
                        }

                        @Override
                        public void onConfigureFailed(@NonNull CameraCaptureSession cameraCaptureSession) {
                        }
                    }, null);
        } catch (Exception e) {
        }
    }

    private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() {
        private void process(CaptureResult result) {
            switch (mState) {
                case STATE_PREVIEW: {
                    break;
                }
                case STATE_WAITING_LOCK: {
                    Integer afState = result.get(CaptureResult.CONTROL_AF_STATE);
                    if (afState == null) {
                        captureStillPicture();
                    } else if (CaptureResult.CONTROL_AF_STATE_FOCUSED_LOCKED == afState
                            || CaptureResult.CONTROL_AF_STATE_NOT_FOCUSED_LOCKED == afState) {
                        Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                        if (null == aeState || aeState == CaptureResult.CONTROL_AE_STATE_CONVERGED) {
                            mState = STATE_PICTURE_TAKEN;
                            captureStillPicture();
                        } else {
                            runPreCaptureSequence();
                        }
                    }
                    break;
                }
                case STATE_WAITING_PRE_CAPTURE: {
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                    if (aeState == null
                            || aeState == CaptureResult.CONTROL_AE_STATE_PRECAPTURE
                            || aeState == CaptureRequest.CONTROL_AE_STATE_FLASH_REQUIRED) {
                        mState = STATE_WAITING_NON_PRE_CAPTURE;
                    }
                    break;
                }
                case STATE_WAITING_NON_PRE_CAPTURE: {
                    Integer aeState = result.get(CaptureResult.CONTROL_AE_STATE);
                    if (aeState == null || aeState != CaptureResult.CONTROL_AE_STATE_PRECAPTURE) {
                        mState = STATE_PICTURE_TAKEN;
                        captureStillPicture();
                    }
                    break;
                }
                default:
                    break;
            }
        }

        @Override
        public void onCaptureProgressed(@NonNull CameraCaptureSession session,
                                        @NonNull CaptureRequest request,
                                        @NonNull CaptureResult partialResult) {
            process(partialResult);
        }

        @Override
        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                       @NonNull CaptureRequest request,
                                       @NonNull TotalCaptureResult result) {
            process(result);
        }
    };

    private void captureStillPicture() {
        try {
            if (null == mCameraDevice) {
                return;
            }
            final CaptureRequest.Builder captureBuilder =
                    mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
            captureBuilder.addTarget(mImageReader.getSurface());
            captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
            captureBuilder.set(CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
            captureBuilder.set(CaptureRequest.FLASH_MODE, CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
            captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
            int rotation = getWindowDisplay().getRotation();
            captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, getOrientation(rotation));
            CameraCaptureSession.CaptureCallback captureCallback =
                    new CameraCaptureSession.CaptureCallback() {
                        @Override
                        public void onCaptureCompleted(@NonNull CameraCaptureSession session,
                                                       @NonNull CaptureRequest request,
                                                       @NonNull TotalCaptureResult result) {
                            unlockFocus();
                        }
                    };
            mCaptureSession.stopRepeating();
            mCaptureSession.capture(captureBuilder.build(), captureCallback, null);
        } catch (Exception e) {
        }
    }

    private Display getWindowDisplay() {
        return ((WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE)).getDefaultDisplay();
    }

    private int getOrientation(int rotation) {
        return (ORIENTATION.get(rotation) + mSensorOrientation + 270) % 360;
    }

    private void setAutoFlash(CaptureRequest.Builder requestBuilder) {
        requestBuilder.set(CaptureRequest.CONTROL_AE_MODE,
                CaptureRequest.CONTROL_AE_MODE_ON_AUTO_FLASH);
    }

    private void unlockFocus() {
        try {
            mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER,
                    CameraMetadata.CONTROL_AF_TRIGGER_CANCEL);
            setAutoFlash(mPreviewRequestBuilder);
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback, mBackgroundHandler);
            mState = STATE_PREVIEW;
            mCaptureSession.setRepeatingRequest(mPreviewRequest, mCaptureCallback, mBackgroundHandler);
        } catch (Exception e) {
        }
    }

    private void runPreCaptureSequence() {
        try {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                mPreviewRequestBuilder.set(CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER,
                        CaptureRequest.CONTROL_AE_PRECAPTURE_TRIGGER_CANCEL);
            } else {
                try {
                    Method setMethod = mPreviewRequestBuilder.getClass().getMethod("set", Class.forName("android.hardware.camera2.CaptureRequest$Key"), Object.class);
                    Class<?> captureRequestClass = Class.forName("android.hardware.camera2.CaptureRequest");
                    Field controlAePreCaptureTriggerField = captureRequestClass.getDeclaredField("CONTROL_AE_PRECAPTURE_TRIGGER");
                    Object controlAePreCaptureTriggerCancel = controlAePreCaptureTriggerField.get(null);
                    setMethod.invoke(mPreviewRequestBuilder, controlAePreCaptureTriggerField.get(null), controlAePreCaptureTriggerCancel);
                } catch (Exception ignore) {
                }
            }
            mState = STATE_WAITING_PRE_CAPTURE;
            mCaptureSession.capture(mPreviewRequestBuilder.build(), mCaptureCallback,
                    mBackgroundHandler);
        } catch (Exception e) {
        }
    }

    @SuppressWarnings("unused")
    private void setUpCameraOutputs(int width, int height) {
        CameraManager manger = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        try {
            for (String cameraId : manger.getCameraIdList()) {
                CameraCharacteristics characteristics = manger.getCameraCharacteristics(cameraId);
                Integer facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                boolean shouldOpenFront = TextUtils.equals(mCameraPosition, CAMERA_POSITION_FRONT);
                if (facing != null &&
                        (shouldOpenFront && facing == CameraCharacteristics.LENS_FACING_BACK
                                || !shouldOpenFront && facing == CameraCharacteristics.LENS_FACING_FRONT)) {
                    continue;
                }
                StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
                if (null == map) {
                    return;
                }
                Size[] cameraSizes = map.getOutputSizes(SurfaceTexture.class);
//                Point displaySize = new Point();
//                getWindowDisplay().getSize(displaySize);
                mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION);
                Arrays.sort(cameraSizes, new CompareSizesByArea());

                boolean swap = (90 == mSensorOrientation || 270 == mSensorOrientation);

                // 选取最合适的尺寸
                Size smartSize = getSmartSize(shouldOpenFront, width, height, cameraSizes, swap);
                Log.e("EEED", "smartSizeW=" + smartSize.getWidth() + " smartSizeH=" + smartSize.getHeight());

//                for (int i = cameraSizes.length - 1; i >= 0; i--) {
//                    int w;
//                    int h;
//                    if (swap) {
//                        w = cameraSizes[i].getHeight();
//                        h = cameraSizes[i].getWidth();
//                    } else {
//                        w = cameraSizes[i].getWidth();
//                        h = cameraSizes[i].getHeight();
//                    }
//                    if (MAX_PREVIEW_WIDTH >= w && MAX_PREVIEW_HEIGHT >= h) {
                if (swap) {
                    mTextureView.setAspectRatio(smartSize.getHeight(), smartSize.getWidth());
                } else {
                    mTextureView.setAspectRatio(smartSize.getWidth(), smartSize.getHeight());
                }
                mPreviewSize = new Size(smartSize.getWidth(), smartSize.getHeight());
                mImageReader = ImageReader.newInstance(smartSize.getWidth(), smartSize.getHeight(), ImageFormat.JPEG, 3);
                mMediaRecorder = new MediaRecorder();
                mCameraId = cameraId;
                return;
//                    }
//                }
            }
        } catch (Exception e) {
        }
    }

    private static class CompareSizesByArea implements Comparator<Size> {
        @Override
        public int compare(Size lhs, Size rhs) {
            return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
        }
    }

    /**
     * 获取最合适的摄像头尺寸
     */
    private Size getSmartSize(boolean isFront, int textureW, int textureH, Size[] cameraSizes, boolean swap) {
        float textureRatio = (float) textureW / textureH;
        Log.d("EEED", "textureW=" + textureW + " textureH=" + textureH + ", textureRatio=" + textureRatio);
        Log.d("EEED", "swap=" + swap);
        Size targetSize = null;
        float targetRatio = 0;
        float ratioDiff = 0;
        int MIN_SIZE = isFront ? 720 : 1080;
        int MAX_SIZE = 1440;
        for (int i = cameraSizes.length - 1; i >= 0; i--) {
            Size size = cameraSizes[i];
            int sizeW = size.getWidth();
            int sizeH = size.getHeight();
            // 小于720可能模糊
            if (sizeW < MIN_SIZE || sizeH < MIN_SIZE) {
                continue;
            }
            float sizeRatio;
            if (swap) {
                // 主流2K屏幕分辨率宽是1440，只取这个范围内的录屏即可，否则录制的视频也会很大
                if (sizeH >= MAX_SIZE) {
                    continue;
                }
                sizeRatio = (float) sizeH / sizeW;
            } else {
                if (sizeW >= MAX_SIZE) {
                    continue;
                }
                sizeRatio = (float) sizeW / sizeH;
            }

            // 比如当前View 宽/高 = 1/3，则需要选取的摄像头尺寸不能小于这个值，比如1/4的话高度4最终会被压扁成3，
            // 如果选择1/2的，相当于预览尺寸高度占了2/3，剩余的部分上下空出位置（黑边）即可，保证比例不被压缩；
            // 但是有时候按这样选出的尺寸可能不是最接近的，比如View 宽/高=0.61，选出的尺寸是0.75的，
            // 但是有个0.6的其实更合适，所以同时保留尺寸小于1/3但是差值小于0.05的尺寸
            if (textureRatio <= sizeRatio || Math.abs(textureRatio - sizeRatio) <= 0.08) {
                float currDiff = textureRatio - sizeRatio;
                Log.e("EEED", "sizeW=" + sizeW + " sizeH=" + sizeH + ", sizeRatio=" + sizeRatio + ", diff=" + currDiff);
                if (targetSize == null) {
                    targetSize = size;
                    targetRatio = sizeRatio;
                    ratioDiff = currDiff;
                } else {
                    if (Math.abs(currDiff) < Math.abs(ratioDiff)) {
                        targetSize = size;
                        targetRatio = sizeRatio;
                        ratioDiff = currDiff;
                    }
                }
            }
        }
        if (targetSize != null) {
            Log.d("EEED", "targetSizeW=" + targetSize.getWidth() + " targetSizeH=" + targetSize.getHeight() + ", targetRatio=" + targetRatio);
            return targetSize;
        }
        return new Size(textureW, textureH);
    }

    private void configureTransform(int viewWidth, int viewHeight) {
        if (null == mTextureView || null == mPreviewSize) {
            return;
        }

        int rotation = getWindowDisplay().getRotation();
        Matrix matrix = new Matrix();
        RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
        RectF bufferRect = new RectF(0, 0, mPreviewSize.getHeight(), mPreviewSize.getWidth());
        float centerX = viewRect.centerX();
        float centerY = viewRect.centerY();

        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);

            float scale = Math.max((float) viewHeight / mPreviewSize.getHeight(),
                    (float) viewWidth / mPreviewSize.getWidth());
            matrix.postScale(scale, scale, centerX, centerY);
            matrix.postRotate(90 * (rotation - 2), centerX, centerY);
        } else if (Surface.ROTATION_180 == rotation) {
            matrix.postRotate(180, centerX, centerY);
        }
        mTextureView.setTransform(matrix);
    }

    interface OnPhoneOrientationChangeListener {
        void onPhoneOrientationChanged(@PhoneOrientation int orientation);
    }
}
